#include "PreCompile.hpp"
#include "NamedObjectTests.hpp"

namespace BuildNG
{
  class SignalTester
  {
  public:
    Glib::ustring expected_old_name;
    Glib::ustring expected_new_name;

    bool pre_name_change_called;
    bool post_name_change_called;

    bool testing_cancel;

    SignalTester()
    {
      pre_name_change_called = false;
      post_name_change_called = false;

      testing_cancel = false;
    }

    void on_pre_name_change(Glib::RefPtr<INamedObject> obj,
      const Glib::ustring &new_name)
    {
      assert(obj->get_name() == expected_old_name);
      assert(new_name == expected_new_name);
      pre_name_change_called = true;

      if(testing_cancel)
        throw Exception("Cancelling name change.");
    }

    void on_post_name_change(Glib::RefPtr<INamedObject> obj,
      const Glib::ustring &old_name,const Glib::ustring &new_name)
    {
      assert(obj->get_name() == expected_new_name);
      assert(old_name == expected_old_name);
      assert(new_name == expected_new_name);
      post_name_change_called = true;
    }
  };

  void NamedObjectTests::test_name_change_signals()
  {
    SignalTester signal_tester;
    signal_tester.expected_old_name = "Kristiania";
    signal_tester.expected_new_name = "Oslo";

    Glib::RefPtr<NamedObject> obj = NamedObject::create("Kristiania");
    obj->pre_name_change.connect(sigc::mem_fun(
      &signal_tester,&SignalTester::on_pre_name_change));
    obj->post_name_change.connect(sigc::mem_fun(
      &signal_tester,&SignalTester::on_post_name_change));

    obj->set_name("Oslo");

    assert(signal_tester.pre_name_change_called);
    assert(signal_tester.post_name_change_called);
    assert(obj->get_name() == "Oslo");
  }

  void NamedObjectTests::test_cancel_name_change()
  {
    SignalTester signal_tester;
    signal_tester.expected_old_name = "Rotterdam";
    signal_tester.expected_new_name = "Rotjeknor";
    signal_tester.testing_cancel = true;

    Glib::RefPtr<NamedObject> obj = NamedObject::create("Rotterdam");
    obj->pre_name_change.connect(sigc::mem_fun(
      &signal_tester,&SignalTester::on_pre_name_change));
    obj->post_name_change.connect(sigc::mem_fun(
      &signal_tester,&SignalTester::on_post_name_change));

    try
    {
      obj->set_name("Rotjeknor");
      assert(!"An exception should have been thrown");
    }
    catch(const Exception &)
    {
    }

    assert(obj->get_name() == "Rotterdam");

    assert(signal_tester.pre_name_change_called);
    assert(!signal_tester.post_name_change_called);
  }

  void NamedObjectTests::test_named_collection_simple()
  {
    NamedObjectCollection<NamedObject> collection;

    collection.add(NamedObject::create("Rotterdam"));
    collection.add(NamedObject::create("Oslo"));
    collection.add(NamedObject::create("Frankfurt"));

    assert(collection.get_size() == 3);
    
    Glib::RefPtr<NamedObject> oslo = collection.try_get("Oslo");
    assert(oslo);
    assert(oslo->get_name() == "Oslo");

    NamedObjectCollection<NamedObject>::Iterator it = collection.get_iterator();
    std::set<Glib::ustring> found_cities;
    while(it.move_next())
    {
      assert(found_cities.find(it.cur()->get_name()) == found_cities.end());
      found_cities.insert(it.cur()->get_name());
    }
    assert(found_cities.size() == 3);
  }

  class NamedObjWithFlag : public NamedObject
  {
  public:
    NamedObjWithFlag(const Glib::ustring &name)
      : NamedObject(name)
    {
    }

    int flag;
  };

  void NamedObjectTests::test_name_collision()
  {
    NamedObjectCollection<NamedObjWithFlag> collection;

    Glib::RefPtr<NamedObjWithFlag> first(new NamedObjWithFlag("Brno"));
    first->flag = 0;
    collection.add(first);

    try
    {
      Glib::RefPtr<NamedObjWithFlag> second(new NamedObjWithFlag("Brno"));
      second->flag = 1;
      collection.add(second);

      assert(!"An exception should have been thrown.");
    }
    catch(const NameCollisionException&)
    {
    }

    assert(collection.get_size() == 1);
    
    Glib::RefPtr<NamedObjWithFlag> in_collection = collection.try_get("Brno");
    assert(in_collection);
    assert(in_collection->get_name() == "Brno");
    assert(in_collection->flag == 0);   // make sure the second one didn't overwrite the first one.
  }

  void NamedObjectTests::test_rename_in_collection()
  {
    NamedObjectCollection<NamedObject> collection;

    Glib::RefPtr<NamedObject> first = NamedObject::create("Nieuw Amsterdam");
    collection.add(first);

    first->set_name("New York");

    Glib::RefPtr<NamedObject> second = NamedObject::create("Nieuw Amsterdam");
    collection.add(second);

    Glib::RefPtr<NamedObject> third = NamedObject::create("Lutetia");
    collection.add(third);
    third->set_name("Paris");

    assert(collection.get_size() == 3);
    assert(collection.try_get("New York")->get_name() == "New York");
    assert(collection.try_get("Nieuw Amsterdam")->get_name() == "Nieuw Amsterdam");
    assert(!collection.try_get("Lutetia"));
  }

  // renaming the same object to the same name should work.
  void NamedObjectTests::test_rename_to_same_name()
  {
    NamedObjectCollection<NamedObject> collection;
    Glib::RefPtr<NamedObject> obj = NamedObject::create("Berlin");
    collection.add(obj);

    obj->set_name("Berlin");
  }
}
